package org.erikaredmark.monkeyshines;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import org.erikaredmark.monkeyshines.bounds.IPoint2D;
/**
*
* Represents the same information as {@code Point2D}, but instances of this class are immutable. These are designed to
* enumerate fixed points that rarely change, or points that will be saved to a file.
* <p/>
* Points that are immutable are internally represented as integers, not floating point. This is because movement calculations
* that may stack over time do not apply if the point does not change.
* <p/>
* This class may not be subclassed.
*
* @author The Doctor
*
*/
public final class ImmutablePoint2D implements IPoint2D {
// Implementation note: Can't be final due to serialization, but class has not public mutator methods.
// These fields should never change after assignment.
private transient int x;
private transient int y;
private transient static final ImmutablePoint2D ZERO_POINT = new ImmutablePoint2D(0, 0);
@Override public int x() {return x;}
@Override public int y() {return y;}
private ImmutablePoint2D(final int x, final int y) {this.x = x; this.y = y; }
public static ImmutablePoint2D of(final int x, final int y) {
if (x == 0 && y == 0) return ZERO_POINT;
else return new ImmutablePoint2D(x, y);
}
/**
*
* Creates an immutable point from a pre-existing mutable point. The newly created immutable point is not affected
* by any changes to the passed point after creation.
* <p/>
* Because the immutable type uses ints and not doubles, double values of the point are truncated (not rounded) and
* stored in the new object.
*
* @param point
* the existing mutable point to create an immutable copy from
*
* @return
* an instance of this object with the x and y fields an integer truncated representation of the double fields
* in the corresponding mutable point. This corresponds to the drawing point.
*
*/
public static ImmutablePoint2D from(final Point2D point) {
return of(point.x(), point.y() );
}
/**
*
* Returns a point that represents this point with only the x-coordinate change to the given value
*
* @param x
* new coordinate
*
* @return
* point representing new location
*
*/
public ImmutablePoint2D newX(int x) {
return of(x, this.y() );
}
/**
*
* Returns a point that represents this point with only the y-coordinate change to the given value
*
* @param y
* new coordinate
*
* @return
* point representing new location
*
*/
public ImmutablePoint2D newY(int y) {
return of(this.x(), y);
}
/**
*
* Applies a multiplication to both x and y of this point and returns a new point. This is generally for scaling
* points between different co-ordinate systems (such as tile co-ordinates vs pixel co-ordinates)
*
* @param scaleX
* scale factour for x size
*
* @param scaleY
* scale factour for y size
*
* @return
* point that represents the original whose components are multiplied by the given parameters
*
*/
public ImmutablePoint2D multiply(int scaleX, int scaleY) {
return of(this.x() * scaleX, this.y() * scaleY);
}
/**
*
* Serialize this {@code ImmutablePoint2D} instance.
*
* @serialData the x co-ordinate (int) and then the y co-ordinate (int)
*
*/
private void writeObject(ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
s.writeInt(x);
s.writeInt(y);
}
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
s.defaultReadObject();
this.x = s.readInt();
this.y = s.readInt();
}
/**
*
* Two points are equal if they share the same x/y co-ordinate.
*
*/
@Override public boolean equals(Object other) {
if (other instanceof ImmutablePoint2D == false) return false;
if (other == this) return true;
return (((ImmutablePoint2D)other).x() == this.x &&
((ImmutablePoint2D)other).y() == this.y );
}
@Override public int hashCode() {
int result = 17;
result += result * 31 + x;
result += result * 31 + y;
return result;
}
@Override public String toString() {
return x + ", " + y;
}
}